package com.zrrd.gmall.cart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zrrd.gmall.cart.mapper.CartInfoMapper;
import com.zrrd.gmall.cart.service.CartAsyncService;
import com.zrrd.gmall.cart.service.CartInfoService;
import com.zrrd.gmall.model.cart.CartInfo;
import com.zrrd.gmall.model.product.SkuInfo;
import com.zrrd.gmall.product.fegin.ProductFeginClient;
import com.zrrd.gmall.service.constant.RedisConst;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartInfoService {

    @Autowired
    private ProductFeginClient productFeginClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CartAsyncService cartAsyncService;

    // 添加购物车
    @Override
    public void addToCart(Long skuId, String userId, Integer num) {
        CartInfo cartInfo;
        //try catch 保证的是Mysql执行操作时候 如果有异常 不继续执行
        try {
            //如果缓存中 没有该Key
            if (!redisTemplate.hasKey(this.getCartKey(userId))) {
                //查询数据库并且 加入缓存
                this.loadCartCache(userId);
            }
            //查询缓存中的购物车对象
            cartInfo = (CartInfo) redisTemplate.boundHashOps(this.getCartKey(userId)).get(skuId.toString());

            //如果不为空
            if (cartInfo != null) {
                cartInfo.setSkuNum(cartInfo.getSkuNum() + num);
                cartInfo.setSkuPrice(productFeginClient.getSkuPrice(skuId));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                //执行更新(异步执行)
                cartAsyncService.updateCartInfo(cartInfo);
            } else {
                //通过fegin请求对象请求商品服务 获取sku基本信息
                SkuInfo skuInfo = productFeginClient.getSkuInfo(skuId);
                //创建购物车对象
                cartInfo = new CartInfo();
                cartInfo.setSkuId(skuId);
                cartInfo.setUserId(userId);
                cartInfo.setCartPrice(skuInfo.getPrice());
                cartInfo.setSkuPrice(skuInfo.getPrice());
                cartInfo.setSkuNum(num);
                cartInfo.setSkuName(skuInfo.getSkuName());
                cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
                cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                //执行保存(异步执行)
                cartAsyncService.saveCartInfo(cartInfo);
            }
        } catch (Exception e) {
            //结束方法
            return;
        }
        //将购物车数据加入到缓存（存入Redis中的是hash类型 对象类型）
        redisTemplate.opsForHash().put(this.getCartKey(userId), skuId.toString(), cartInfo);
        //设置过期时间
        this.setCartKeyExpire(this.getCartKey(userId));

    }

    //查询购物车
    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        //定义最终返回的购物车集合数据
        List<CartInfo> cartInfoList = new ArrayList<>();
        //如果用户没有登录
        if (StringUtils.isEmpty(userId)) {
            //返回临时用户的购物车数据
            cartInfoList = this.getCartList(userTempId);
            return cartInfoList;
        }
        //如果用户登录了
        if (!StringUtils.isEmpty(userId)) {
            //获取临时购物车数据
            List<CartInfo> cartTempList = this.getCartList(userTempId);
            //如果临时购物车有数据
            if (!CollectionUtils.isEmpty(cartTempList)) {
                //进行合并 返回合并后的购物车集合
                cartInfoList = this.mergeToCartList(cartTempList, userId);
                //删除临时购物车数据
                this.deleteCartList(userTempId);
            }
            //如果临时购物车中没有数据
            if (StringUtils.isEmpty(userTempId) || CollectionUtils.isEmpty(cartTempList)) {
                //返回当前登录的用户购物车数据
                cartInfoList = this.getCartList(userId);
            }

        }
        return cartInfoList;
    }

    //更新选中状态
    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        //执行异步更新
        cartAsyncService.checkCart(userId, isChecked, skuId);

        //获取缓存中的Key
        String cartKey = this.getCartKey(userId);
        //根据Key获取缓存中的Hash对象
        BoundHashOperations<String, String, CartInfo> boundHashOperations = redisTemplate.boundHashOps(cartKey);
        //判断是否有有指定的key
        if (boundHashOperations.hasKey(skuId.toString())) {
            //根据小key获取数据
            CartInfo cartInfo = boundHashOperations.get(skuId.toString());
            //给购物车对象 重新设置状态
            cartInfo.setIsChecked(isChecked);
            //更新缓存
            boundHashOperations.put(skuId.toString(), cartInfo);
            //设置过期时间
            this.setCartKeyExpire(cartKey);
        }

    }

    //删除购物车中指定商品
    @Override
    public void deleteCartInfo(String userId, Long skuId) {
        //执行异步删除
        cartAsyncService.deleteCartInfo(userId, skuId);
        //获取缓存Key
        String cartKey = this.getCartKey(userId);
        //获取缓存对象
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(cartKey);
        if (boundHashOperations.hasKey(skuId.toString())) {
            boundHashOperations.delete(skuId.toString());
        }
    }

    //获取用户选中的购物车数据
    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        //创建最终返回的集合
        List<CartInfo> cartInfoList = new ArrayList<>();
        //从缓存中获取
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartCacheList = redisTemplate.opsForHash().values(cartKey);
        //非空判断
        if(!CollectionUtils.isEmpty(cartCacheList)){
            for (CartInfo cartInfo : cartCacheList) {
                //如果是选中 添加进选中的集合
                if(cartInfo.getIsChecked().intValue() == 1){
                    cartInfoList.add(cartInfo);
                }
            }
        }
        //返回选中的集合
        return cartInfoList;
    }

    //合并购物车的方法
    private List<CartInfo> mergeToCartList(List<CartInfo> cartTempList, String userId) {
        //获取当前登录用户的购物车数据
        List<CartInfo> cartList = this.getCartList(userId);
        //将该集合拆解成一个Map集合 key：skuId value 对应的cartInfo
        Map<Long, CartInfo> infoMap = cartList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
        //遍历临时用户的购物车集合
        for (CartInfo cartInfo : cartTempList) {
            //获取当前购物车对象中的SkuId
            Long skuId = cartInfo.getSkuId();
            //判断map集合中是否包含指定的key（当前临时购物车数据 在登录的用户购物车数据 中 该sku商品是否存在）
            if (infoMap.containsKey(skuId)) {
                //获取该条数据
                CartInfo cartInfoLogin = infoMap.get(skuId);
                //更新数量和更新更新日期
                cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum() + cartInfo.getSkuNum());
                cartInfoLogin.setUpdateTime(new Timestamp(new Date().getTime()));
                //如果临时中 是选中状态 登录的数据也设置为选中
                if (cartInfoLogin.getIsChecked().intValue() == 1) {
                    cartInfoLogin.setIsChecked(1);
                }
                //创建条件构造器
                QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
                wrapper.eq("user_id", userId);
                wrapper.eq("sku_id", cartInfoLogin.getSkuId());
                //执行更新
                this.update(cartInfoLogin, wrapper);
            } else {
                //如果临时购物车中的该商品 在登录用户的购物车数据中 不存在
                cartInfo.setUserId(userId);
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                //执行保存
                this.save(cartInfo);
            }
        }

        //查询数据库 更新Redis缓存
        List<CartInfo> cartInfoList = this.loadCartCache(userId);
        return cartInfoList;
    }

    //删除临时用户的购物车
    private void deleteCartList(String userTempId) {
        //异步删除购物车
        cartAsyncService.deleteCartInfo(userTempId);

        //从Redis缓存中删除
        String cartKey = this.getCartKey(userTempId);
        if (redisTemplate.hasKey(cartKey)) {
            redisTemplate.delete(cartKey);
        }
    }


    //查询购物车重载方法（根据用户ID查询->可以是临时的 可以是登录的）
    private List<CartInfo> getCartList(String userId) {
        //创建最终返回的购物车列表
        List<CartInfo> cartInfoList = new ArrayList<>();
        //如果传递过来的用户ID为空
        if (StringUtils.isEmpty(userId)) {
            //返回空集合
            return cartInfoList;
        }
        //获取缓存定义的Key
        String cartKey = this.getCartKey(userId);
        //从缓存中获取数据当前用户购物车集合
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        //非空判断
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            //直接返回
            return cartInfoList;
        } else {
            //缓存没有数据 查询数据库 存入缓存
            cartInfoList = this.loadCartCache(userId);
            return cartInfoList;
        }
    }

    //根据用户Id查询购物车数据库 存入缓存的方法
    @Override
    public List<CartInfo> loadCartCache(String userId) {
        QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId);
        //查询
        List<CartInfo> cartInfoList = this.list(wrapper);
        //判断数据库中是否有当前用户的购物车集合数据
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return cartInfoList;
        }
        //将数据库中的数据存入缓存
        Map<String, CartInfo> map = new HashMap<>();
        //遍历数据库中购物车的集合(为了获取一次最新价格)
        for (CartInfo cartInfo : cartInfoList) {
            //获取当前购物车的商品最新价格
            BigDecimal skuPrice = productFeginClient.getSkuPrice(cartInfo.getSkuId());
            //设置最新价格
            cartInfo.setSkuPrice(skuPrice);
            //将当前对象存入到Map集合
            map.put(cartInfo.getSkuId().toString(), cartInfo);
        }
        //存入Redis缓存
        redisTemplate.opsForHash().putAll(this.getCartKey(userId), map);
        //设置过期时间
        this.setCartKeyExpire(this.getCartKey(userId));
        //返回数据
        return cartInfoList;
    }


    //定义Redis缓存中的Key
    private String getCartKey(String userId) {
        //user:666:cart
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }

    //设置Redis缓存的过期时间
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }
}
